package tman.system.peer;

import Monitor.MonitorClient;
import Monitor.MonitorClientInit;
import common.configuration.CyclonConfiguration;
import common.configuration.MonitorConfiguration;
import common.configuration.TManConfiguration;
import common.events.PeerDataRequest;
import common.events.PeerDataResponse;
import cyclon.system.peer.cyclon.CyclonPartnersPort;
import java.util.LinkedList;
import java.util.Set;

import common.peer.JoinTMan;
import common.peer.JoinPeer;
import common.peer.PeerAddress;
import common.peer.PeerInit;
import common.peer.PeerPort;
import common.simulation.PeerStatus;
import common.web.WebApplication;
import common.web.WebApplicationInit;
import cyclon.system.peer.cyclon.*;

import se.sics.kompics.Component;
import se.sics.kompics.ComponentDefinition;
import se.sics.kompics.Handler;
import se.sics.kompics.Negative;
import se.sics.kompics.Positive;
import se.sics.kompics.address.Address;
import se.sics.kompics.network.Network;
import se.sics.kompics.p2p.bootstrap.BootstrapCompleted;
import se.sics.kompics.p2p.bootstrap.BootstrapConfiguration;
import se.sics.kompics.p2p.bootstrap.BootstrapRequest;
import se.sics.kompics.p2p.bootstrap.BootstrapResponse;
import se.sics.kompics.p2p.bootstrap.P2pBootstrap;
import se.sics.kompics.p2p.bootstrap.PeerEntry;
import se.sics.kompics.p2p.bootstrap.client.BootstrapClient;
import se.sics.kompics.p2p.bootstrap.client.BootstrapClientInit;
import se.sics.kompics.timer.Timer;
import se.sics.kompics.web.Web;

import tman.simulator.snapshot.Snapshot;
import tman.system.peer.tman.TMan;
import tman.system.peer.tman.TManInit;

public final class Peer extends ComponentDefinition {
	
	Negative<PeerPort> peerPort = negative(PeerPort.class);
	Positive<Network> network = positive(Network.class);
	Positive<Timer> timer = positive(Timer.class);
	Negative<Web> web = negative(Web.class);

	private Component cyclon, tman, bootstrap, monitor, webapp;
	private Address self;
	private PeerAddress peerSelf;
	private int bootstrapRequestPeerCount;
	private boolean bootstrapped;
	private TManConfiguration tmanConfiguration;
	private BootstrapConfiguration bootstrapConfiguration;
	private MonitorConfiguration monitorConfiguration;

//-------------------------------------------------------------------	
	public Peer() {
		tman = create(TMan.class);
		cyclon = create(Cyclon.class);
		bootstrap = create(BootstrapClient.class);
		monitor = create(MonitorClient.class);
		webapp = create(WebApplication.class);
		
		connect(network, tman.getNegative(Network.class));
		connect(network, cyclon.getNegative(Network.class));
		connect(network, bootstrap.getNegative(Network.class));
		connect(network, monitor.getNegative(Network.class));
		connect(timer, tman.getNegative(Timer.class));
		connect(timer, cyclon.getNegative(Timer.class));
		connect(timer, bootstrap.getNegative(Timer.class));
		connect(timer, monitor.getNegative(Timer.class));

		connect(web, webapp.getPositive(Web.class));
		connect(tman.getPositive(PeerStatus.class), webapp
				.getNegative(PeerStatus.class));
		connect(tman.getPositive(PeerStatus.class), monitor
				.getNegative(PeerStatus.class));
		
		connect(cyclon.getPositive(CyclonPartnersPort.class), tman.getNegative(CyclonPartnersPort.class));
		
		subscribe(handleInit, control);
		subscribe(handleJoin, peerPort);
		subscribe(handleJoinTMan, peerPort);
		subscribe(handleJoinCompleted, cyclon.getPositive(CyclonPort.class));
		subscribe(handleBootstrapResponse, bootstrap.getPositive(P2pBootstrap.class));
		
		subscribe(handlePeerDataRequest, peerPort);
		subscribe(handlePeerDataResponse, tman.getPositive(PeerStatus.class));
	}

//-------------------------------------------------------------------	
	Handler<PeerInit> handleInit = new Handler<PeerInit>() {
		public void handle(PeerInit init) {
			peerSelf = init.getPeerSelf();
			if(peerSelf.isSource())
				System.out.println("I'M A SOURCE NODE!");
			self = peerSelf.getPeerAddress();
			CyclonConfiguration cyclonConfiguration = init.getCyclonConfiguration();
			tmanConfiguration = init.getTManConfiguration();
			bootstrapConfiguration = init.getBootstrapConfiguration();
			monitorConfiguration = init.getMonitorConfiguration();
			
			bootstrapRequestPeerCount = cyclonConfiguration.getBootstrapRequestPeerCount();

			trigger(new CyclonInit(cyclonConfiguration), cyclon.getControl());
			trigger(new BootstrapClientInit(self, bootstrapConfiguration), bootstrap.getControl());
			trigger(new MonitorClientInit(monitorConfiguration,
					peerSelf), monitor.getControl());
		}
	};

//-------------------------------------------------------------------	
	Handler<JoinPeer> handleJoin = new Handler<JoinPeer>() {
		public void handle(JoinPeer event) {
			trigger(new WebApplicationInit(peerSelf,
					monitorConfiguration.getMonitorServerAddress(),
					bootstrapConfiguration.getBootstrapServerAddress(),
					monitorConfiguration.getClientWebPort()), webapp
					.getControl());
			
			//System.out.println("I'm sending a bootstrap request!");
			BootstrapRequest request = new BootstrapRequest("Cyclon", bootstrapRequestPeerCount);
			trigger(request, bootstrap.getPositive(P2pBootstrap.class));
			
			//Snapshot.addPeer(peerSelf);
		}
	};

//-------------------------------------------------------------------	
	Handler<BootstrapResponse> handleBootstrapResponse = new Handler<BootstrapResponse>() {
		public void handle(BootstrapResponse event) {
			if (!bootstrapped) {
				Set<PeerEntry> somePeers = event.getPeers();
					//System.out.println("peerSelf: " + peerSelf.getPeerId() + " boostrapr: " + somePeers);
				LinkedList<PeerAddress> cyclonInsiders = new LinkedList<PeerAddress>();
				for (PeerEntry peerEntry : somePeers)
					cyclonInsiders.add((PeerAddress) peerEntry.getOverlayAddress());	
				trigger(new CyclonJoin(peerSelf, cyclonInsiders), cyclon.getPositive(CyclonPort.class));
				bootstrapped = true;
				
			}
		}
	};

//-------------------------------------------------------------------	
	Handler<JoinCompleted> handleJoinCompleted = new Handler<JoinCompleted>() {
		public void handle(JoinCompleted event) {
			trigger(new BootstrapCompleted("Cyclon", peerSelf), bootstrap.getPositive(P2pBootstrap.class));
			//trigger(new TManInit(peerSelf, tmanConfiguration), tman.getControl());
		}
	};
	
	
//-------------------------------------------------------------------
	Handler<JoinTMan> handleJoinTMan = new Handler<JoinTMan>() {
		public void handle(JoinTMan event) {
			trigger(new TManInit(peerSelf, tmanConfiguration), tman.getControl());
		}
	};	
//-------------------------------------------------------------------Edited
	
	Handler<PeerDataRequest> handlePeerDataRequest = new Handler<PeerDataRequest>() {
		public void handle(PeerDataRequest event) {
			trigger(event, tman.getPositive(PeerStatus.class));
		}
	};

	Handler<PeerDataResponse> handlePeerDataResponse = new Handler<PeerDataResponse>() {
		public void handle(PeerDataResponse event) {
			trigger(event, peerPort);
		}
	};
}
